package com.bondex.charter.service;

import com.bondex.charter.commons.Const;
import com.bondex.charter.dao.CharterBookingMapper;
import com.bondex.charter.dao.CharterSiMapper;
import com.bondex.charter.po.CharterBooking;
import com.bondex.charter.po.CharterSi;
import com.bondex.charter.pojo.ContainerInfo;
import com.bondex.global.pojo.PageTableParamT;
import com.bondex.global.utils.JacksonUtil;
import com.bondex.global.utils.Tools;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 服务层基类
 * Created by JD on 2019/05/05.
 */
@Service
public class BaseService {

	//region 常量

	protected Logger log = LoggerFactory.getLogger(BaseService.class);

	@Resource
	protected CharterBookingMapper charterBookingMapper;
	@Resource
	protected CharterSiMapper charterSiMapper;

	//endregion

	//region 内部使用方法

	//取得每页条数，默认10，最大100
	protected int getPageCount(int nPageCount) {
		try {
			if (nPageCount == -256) {
				nPageCount = 0;
			} else if (nPageCount <= 0 || nPageCount > 100) {
				nPageCount = 10;
			}
		} catch (Exception e) {
			nPageCount = 10;
		}
		return nPageCount;
	}

	protected int getPageCount(String sPageCount) {
		int nPageCount = 10;
		try {
			nPageCount = Integer.parseInt(sPageCount);
			nPageCount = getPageCount(nPageCount);
		} catch (Exception e) {
			nPageCount = 10;
		}
		return nPageCount;
	}


	//取得当前页号
	protected int getPageNumber(int nPageNumber) {
		return nPageNumber <= 0 ? 1 : nPageNumber;
	}

	protected int getPageNumber(String sPageNumber) {
		int nPageNumber = 1;
		try {
			nPageNumber = Integer.parseInt(sPageNumber);
		} catch (Exception e) {
			nPageNumber = 1;
		}
		return getPageNumber(nPageNumber);

	}

	protected void initPageTableParam(PageTableParamT oParam) {
		if (oParam == null) oParam = new PageTableParamT();

		//每页条数
		oParam.setPageCount(getPageCount(oParam.getPageCount()));

		//要获取第几页
		oParam.setPageNumber(getPageNumber(oParam.getPageNumber()));

		//计算得到起始行
		oParam.setStartIndex((oParam.getPageNumber() - 1) * oParam.getPageCount());

		//排序规则
		if (StringUtils.isEmpty(oParam.getOrderColumn())) {
			oParam.setOrderColumn("");
			oParam.setOrderDirect("");
		} else {
			if (!"desc".equalsIgnoreCase(oParam.getOrderDirect())) {
				oParam.setOrderDirect("asc");
			}
		}

	}

	//入参的值为空判断
	protected boolean isPropertyEmpty(Object oParam, String sKey) throws Exception {
		Object oValue = PropertyUtils.getProperty(oParam, sKey);
		return oValue == null || StringUtils.isEmpty((String) oValue);
	}

	//台账查询使用，将单号字符串，拆分为单号列表。仅在结果大于1个时返回
	protected List<String> splitBillNo4Search(String sBillNo) {
		List<String> oResult = StringUtils.isNotBlank(sBillNo)
				? Tools.splitStringTrim(sBillNo, "[ ;:,.；：，。\t\n\r]")
				: null;
		return (oResult != null && oResult.size() > 1)
				? oResult : null;
	}

	//endregion

	//region 反射方式获取值

	/**
	 * 获取用于反射的字段
	 */
	protected Field getFieldInClass(Class cl, String sFieldName) throws Exception {
		Field f = null;
		if (f == null) {
			try {
				f = cl.getDeclaredField(sFieldName);
			} catch (Exception e) {
			}
		}
		if (f == null) {
			try {
				f = cl.getField(sFieldName);
			} catch (Exception e) {
			}
		}
		if (f == null) {
			try {
				f = cl.getSuperclass().getDeclaredField(sFieldName);
			} catch (Exception e) {
			}
		}
		if (f == null) {
			try {
				f = cl.getSuperclass().getSuperclass().getDeclaredField(sFieldName);
			} catch (Exception e) {
			}
		}

		if (f != null) {
			f.setAccessible(true);
		}
		return f;
	}

	/**
	 * 通过反射的方式获取相关值
	 */
	protected <T> Integer getID(T oRecord, Class<T> cl) throws Exception {
		Field fieldID = getFieldInClass(cl, "id");
		return (Integer) fieldID.get(oRecord);
	}

	protected <T> String getStringByField(T oRecord, Class<T> cl, String sFieldName) throws Exception {
		Field oField = getFieldInClass(cl, sFieldName);
		return (String) oField.get(oRecord);
	}

	/**
	 * 获取ID对应的列表
	 */
	protected List<Integer> getIDs(List oSrc) throws Exception {
		return getFieldValues(oSrc, "id", Integer.class);
	}

	/**
	 * 将列表中的某值取出
	 *
	 * @param list       列表
	 * @param sFieldName 值对应的字段名
	 * @param oClass     泛型类型
	 * @param <T>        泛型
	 * @return 泛型列表
	 * @throws Exception
	 */
	protected <T> List<T> getFieldValues(List list, String sFieldName, Class<T> oClass) throws Exception {
		List<T> oResult = new ArrayList<>();
		if (list != null) {
			for (Object o : list) {
				Class<?> cl = o.getClass();
				Field oField = getFieldInClass(cl, sFieldName);

				if (oField != null) {
					oResult.add((T) oField.get(o));
				}
			}
		}
		return oResult;
	}

	/**
	 * 去除列表中无主键的项
	 */
	protected void removeNoID(List list) throws Exception {
		removeNoID(list, "id");
	}

	protected void removeNoID(List list, String sFieldName) throws Exception {
		if (list != null) {
			for (int i = list.size() - 1; i >= 0; i--) {
				Object o = list.get(i);

				Class<?> cl = o.getClass();
				Field oField = getFieldInClass(cl, sFieldName);

				if (oField == null) {
					list.remove(i);
				} else {
					int nID = (int) oField.get(o);
					if (nID <= 0) {
						list.remove(i);
					}
				}
			}
		}
	}

	//endregion

	//region 数据转换

	//将数据查询出来的PGobject对象转换为程序中使用的制定对象
	protected <T> T convertObject2Bean(Object oSrc, TypeReference oType) {
		if (oSrc == null) {
			return null;
		} else if (oSrc instanceof String) {
			return (T) JacksonUtil.readValue((String) oSrc, oType);
		} else {
			return (T) oSrc;
		}
	}

	public static <T> T convertObject2Bean(Object oSrc, Class<T> oClass) {
		if (oSrc == null) {
			return null;
		} else if (oSrc instanceof String) {
			return (T) JacksonUtil.readValue((String) oSrc, oClass);
		} else {
			return (T) oSrc;
		}
	}

	/**
	 * 转换为Json字符串，第一个有值用第一个的，第二个有值用第二个的
	 * 如果值为字符串，直接使用
	 */
	protected String convert2JsonInfo4DB(Object oFirst, Object oSecond, boolean bInsert, String sDefault) throws Exception {
		String s = null;
		if (oFirst != null) {
			if (oFirst instanceof String) {
				s = (String) oFirst;
			} else {
				s = JacksonUtil.writeValue(oFirst);
			}
		} else if (oSecond != null) {
			if (oSecond instanceof String) {
				s = (String) oSecond;
			} else {
				s = JacksonUtil.writeValue(oSecond);
			}
		}

		/**
		 * 如果未找到出参，判断是否为插入语句准备的
		 * 如果是插入语句，使用默认值返回，一般为 {} 或者 []
		 * 如果不是插入语句，按照null返回，防止将默认值更新到数据库中
		 */
		if (StringUtils.isBlank(s)) {
			if (bInsert) {
				if (StringUtils.isNotBlank(sDefault)) {
					s = sDefault;
				}
			} else {
				s = null;
			}
		}
		return s;
	}

	/**
	 * 转换为Json字符串
	 *
	 * @param oSrc     对象
	 * @param sDefault 默认字符串
	 * @param bCreate  创建还是更新
	 * @return
	 * @throws Exception
	 */
	protected String convertObject2JsonStr(Object oSrc, String sDefault, boolean bCreate) throws Exception {
		return (oSrc != null)
				? JacksonUtil.writeValue(oSrc)
				: (bCreate ? sDefault : null)
				;
	}

	protected String convertObject2JsonStr(Object oSrc, String sDefault) throws Exception {
		return convertObject2JsonStr(oSrc, sDefault, true);
	}

	/**
	 * 将箱型箱量，转换为箱型箱量的描述 2X20GP
	 */
	protected String convertContainerNoAndType(List<ContainerInfo> oContainers) throws Exception {
		String sContainer = "";
		if (oContainers != null && oContainers.size() > 0) {
			//获取箱子类型列表&每个类型下箱子的个数
			List<String> oContainerTypes = new ArrayList<>();
			Map<String, Integer> oContainerGroup = new HashMap<>();
			for (ContainerInfo o : oContainers) {
				if (o == null) continue;
				if (StringUtils.isBlank(o.getContainerType())) {
					//todo 通过22G1获取
				}

				oContainerTypes.add(o.getContainerType());
				Tools.plusStepMap(oContainerGroup, o.getContainerType(), 1);
			}

			//箱子类型列表去重并排序
			Tools.removeDuplicate(oContainerTypes);

			//拼接返回字符串
			for (String s : oContainerTypes) {
				sContainer += String.valueOf(oContainerGroup.get(s)) + "X" + s + ",";
			}
			if (sContainer.length() > 0) {
				sContainer = sContainer.substring(0, sContainer.length() - 1);
			}
		}
		return sContainer;
	}

	//endregion

	//region 轻重货判断

	/**
	 * 轻货：8000kgs/箱(不分箱型) 分箱重量字段底色标绿
	 * 重货：20000kgs/箱(不分箱型) 分箱重量字段底色标红
	 *
	 * 订舱按照毛重除箱量计算
	 * 截单按照每一个箱子的毛重自行比较，单记录的重货标红轻货标绿。列表中有任何为重货，则该截单为重货
	 */

	/**
	 * 根据订舱报文判断轻重货
	 */
	protected Byte getBulkyHeavyFlagOfBooking(CharterBooking oBooking) throws Exception {
		Byte nFlag = Const.BHF_NOMAL;

		List<ContainerInfo> oContainerList = oBooking.getContainerInfos();
		if (oContainerList == null || oContainerList.size() <= 0) {
			if (oBooking.getContainerInfo() != null) {
				oContainerList = JacksonUtil.readValue((String) oBooking.getContainerInfo(), new TypeReference<List<ContainerInfo>>() {
				});
			}
		}

		int nContainerCount = oContainerList != null ? oContainerList.size() : 0;
		BigDecimal nWeight = oBooking.getGrossWeight() != null ? oBooking.getGrossWeight() : new BigDecimal(0);

		if (nWeight.compareTo(new BigDecimal(0)) > 0 && nContainerCount > 0) {
			BigDecimal nSingleWeight = nWeight.divide(new BigDecimal(nContainerCount));

			if (nSingleWeight.compareTo(new BigDecimal(20000)) > 0) {
				nFlag = Const.BHF_HEAVY;
			} else if (nSingleWeight.compareTo(new BigDecimal(8000)) < 0) {
				nFlag = Const.BHF_BULKY;
			}
		}
		return nFlag;
	}

	/**
	 * 根据订舱委托号，获取订舱信息，然后判断轻重货
	 */
	protected Byte getBulkyHeavyFlagOfBooking(String sOrderNo) throws Exception {
		//取值
		CharterBooking oBooking = charterBookingMapper.selectByOrderNo(sOrderNo);

		return getBulkyHeavyFlagOfBooking(oBooking);
	}

	/**
	 * 根据订舱委托号，处理数据库中的轻重货状态
	 */
	protected void refreshBulkyHeavyFlagOfBooking(String sOrderNo) throws Exception {
		//取值
		CharterBooking oBooking = charterBookingMapper.selectByOrderNo(sOrderNo);

		Byte nFlag = getBulkyHeavyFlagOfBooking(oBooking);

		charterBookingMapper.updateBulkyHeavyFlag(sOrderNo, nFlag);
	}


	/**
	 * 判断单箱的轻重货
	 */
	protected Byte getBulkyHeavyFlagOfContainer(ContainerInfo oContainerInfo) throws Exception {
		Byte nFlag = Const.BHF_NOMAL;

		BigDecimal nWeight = oContainerInfo != null && oContainerInfo.getWeight() != null
				? oContainerInfo.getWeight() : new BigDecimal(0);

		if (nWeight.compareTo(new BigDecimal(20000)) > 0) {
			nFlag = Const.BHF_HEAVY;
		} else if (nWeight.compareTo(new BigDecimal(8000)) < 0) {
			nFlag = Const.BHF_BULKY;
		}

		return nFlag;
	}

	/**
	 * 根据截单报文判断轻重货
	 */
	protected Byte getBulkyHeavyFlagOfSi(CharterSi oSi) throws Exception {
		Byte nFlag = Const.BHF_NOMAL;

		List<ContainerInfo> oContainerList = JacksonUtil.readValue((String) oSi.getContainerInfo(), new TypeReference<List<ContainerInfo>>() {
		});

		if (oContainerList != null && oContainerList.size() > 0) {
			boolean bHasHeavy = false;
			boolean bHasBulky = false;
			for (ContainerInfo o : oContainerList) {
				Byte nSingleFlag = getBulkyHeavyFlagOfContainer(o);
				if ((int) nSingleFlag == (int) Const.BHF_HEAVY) {
					bHasHeavy = true;
					break;
				} else if ((int) nSingleFlag == (int) Const.BHF_BULKY) {
					bHasBulky = true;
				}
			}

			if (bHasHeavy) {
				nFlag = Const.BHF_HEAVY;
			} else if (bHasBulky) {
				nFlag = Const.BHF_BULKY;
			}
		}

		return nFlag;
	}

	/**
	 * 根据截单委托号，获取截单信息，然后判断轻重货
	 */
	protected Byte getBulkyHeavyFlagOfSi(String sOrderNo) throws Exception {
		//取值
		CharterSi oSi = charterSiMapper.selectByOrderNo(sOrderNo);

		return getBulkyHeavyFlagOfSi(oSi);
	}

	/**
	 * 根据截单委托号，处理数据库中的轻重货状态
	 */
	protected void refreshBulkyHeavyFlagOfSi(String sOrderNo) throws Exception {
		//取值
		CharterSi oSi = charterSiMapper.selectByOrderNo(sOrderNo);

		Byte nFlag = getBulkyHeavyFlagOfSi(oSi);

		charterSiMapper.updateBulkyHeavyFlag(sOrderNo, nFlag);
	}

	//endregion

}
